using Fusion.Utilities.Bit;
using Fusion.Utilities.Generics;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;

namespace Fusion.Net.UDP
{
    public class OrderUDPClientSocket : UDPSocket
    {
        OrderDictionary<string, DataCache> dataCaches = new OrderDictionary<string, DataCache>();
        OrderDictionary<string, int> sendSeqs = new OrderDictionary<string, int>();

        public OrderUDPClientSocket(UDPNet udp, AddressFamily addressFamily) : base(udp, addressFamily)
        {
        }

        public OrderUDPClientSocket(UDPNet udp, Socket socket) : base(udp, socket)
        {
        }


        protected override void Invoke()
        {
            EndPoint receiveIP = new IPEndPoint(Socket.AddressFamily == AddressFamily.InterNetworkV6 ? IPAddress.IPv6Any : IPAddress.Any, 0);
            int length = Socket.ReceiveFrom(buffer, ref receiveIP);
            byte[] data = new byte[length];
            Array.Copy(buffer, 0, data, 0, length);

            DataCache dataCache;
            lock (dataCaches)
            {
                if (!dataCaches.TryGetValue(receiveIP.ToString(), out dataCache))
                {
                    dataCache = new DataCache(receiveIP, 10000);
                    dataCaches.Add(receiveIP.ToString(), dataCache);
                }
            }

            UnPack(data, dataCache);
        }
        public override byte[] Pack(byte[] data, int length)
        {
            int sendSeq;
            string key = TargetIP.ToString();
            lock (sendSeqs)
            {
                if (!sendSeqs.TryGetValue(key, out sendSeq))
                {
                    sendSeq = 0;
                    sendSeqs.Add(key, 0);
                }
                sendSeqs[key] = sendSeq + 1;
            }


            byte[] sendBuffer = new byte[data.Length + 4];
            BitUtility.WriteInt(sendBuffer, 0, sendBuffer.Length);
            Array.Copy(data, 0, sendBuffer, 4, data.Length);
            return sendBuffer;
        }

        public void UnPack(byte[] data, DataCache dataCache)
        {
            int offset = 4;
            int seq = BitUtility.ReadInt(data, 0);

            int dataLength = data.Length - offset;
            byte[] dataArray = new byte[dataLength];
            Array.Copy(data, offset, dataArray, 0, dataLength);

            List<byte[]> timeOutDatas = dataCache.Push(seq, dataArray);

            if (timeOutDatas != null && timeOutDatas.Count > 0)
            {
                for (int i = 0; i < timeOutDatas.Count; i++)
                {
                    UDPClientSocket socket = new UDPClientSocket(udp, null);
                    socket.Bind(dataCache.IP);
                    netBase.DataHandler.Enqueue(socket, new NetDataHandler.DataWrap(timeOutDatas[i]));
                }
            }

            byte[] result;
            dataCache.Pull(out result);
            if (result != null)
            {
                UDPClientSocket socket = new UDPClientSocket(udp, null);
                socket.Bind(dataCache.IP);
                netBase.DataHandler.Enqueue(socket, new NetDataHandler.DataWrap(result));
            }
        }

        public override void ClearCaches(string ip)
        {
            string key = ip;
            lock (dataCaches)
            {
                dataCaches.Remove(key);
            }
            lock (sendSeqs)
            {
                sendSeqs.Remove(key);
            }
        }
    }
}

